package generic.genetic.chromosome;

import java.util.ArrayList;
import java.util.Random;

import p3.structures.Tree;

public class TreeChromosome extends Chromosome{

	protected Tree tree;
	protected int max_depth;
	
	public float evaluate(){ return 0;}

	public TreeChromosome(int depth){
		this.nGenes = 1;
		tree = new Tree();
		max_depth = depth;
	}
	
	public TreeChromosome(TreeChromosome c){
		this.nGenes = 1;
		this.tree = c.getTree();
		this.max_depth = c.max_depth;
	}
	
	@Override
	public void randomInit(int type) {
		tree.generateChilds(type, max_depth);
	}



	@Override
	public void cross(int type, Chromosome c) {
		cross(type, (TreeChromosome) c);
	}

	@Override
	public boolean mutation(int type, double mutation_rate) {
		
		Random r = new Random();
		if (r.nextFloat() >= mutation_rate) return false;
		
		switch(type){
		case 0: mutation_terminal(); break;
		case 1: mutation_functional(); break;
		case 2: mutation_initialization(); break;
		case 3: mutation_permutation(); break;
		}
		
		return true;
	}
	
	public void cross(int type, TreeChromosome c){
		switch(type){
		case 0: cross_subTree(c); break;
		}
	}
	

	private void cross_subTree(TreeChromosome c) {
		
		Tree node_1 = tree.getRandomNode();
		Tree node_2 = c.tree.getRandomNode();
		Tree parent_1 = node_1.getParent();
		Tree parent_2 = node_1.getParent();
		//TODO Revisar
		if (parent_1 == null) parent_1 = node_1; 
		if(parent_2 == null) parent_2 = node_2;
		parent_1.replace(node_1, node_2);
		parent_2.replace(node_2, node_1);
	}


	private void mutation_terminal() {
		Tree pointer = tree;
		while (!pointer.isLeaf()){
			pointer = pointer.getRandomChild();
		}
		pointer.getNode().setDifferentTerminal();
	}

	
	/**
	 * Changes a random node (function) by a different one
	 * If there's not, then does nothing.
	 */
	private void mutation_functional() {
		Tree pointer = tree.getRandomNode();
		if (pointer.isLeaf()) pointer = pointer.getParent();
		if (pointer != null) pointer.getNode().setDifferentFunction();
	}
	
	private void mutation_initialization() {
		Tree pointer = tree.getRandomNode();
		Tree parent = pointer.getParent();
		if (parent == null) parent = pointer;
		Tree newTree = new Tree();
		newTree.generateChilds(1, 2);
		parent.replace(pointer, newTree);
	}
	
	private void mutation_permutation() {
		Tree pointer = tree.getRandomNode();
		if (pointer.isLeaf() && pointer.getParent() != null) pointer = pointer.getParent();
		ArrayList<Tree> trees = new ArrayList<>();
		for (int i=0;i<pointer.getNumChild();i++) trees.add(pointer.getChild(i));
		Tree perm_trees[] = new Tree[pointer.getNumChild()];
		Random r = new Random();
		int pos;
		for (int i=0;i<pointer.getNumChild();i++){
			pos = r.nextInt(pointer.getNumChild()-i);
			perm_trees[i] = trees.get(pos);
			trees.remove(pos);
		}
		pointer.setChilds(perm_trees);
	}
		
	public Tree getTree() { return tree.getCopy(); }

	
	public void setMaxDepth(int depth) {
		max_depth = depth;
	}
	
	@Override
	public Chromosome getCopy() { return new TreeChromosome(this); }

	@Override
	public String toString() {
		String s = tree.toString();
		return s;
	}

}
